#ifndef __MY_VECTOR_H__
#define __MY_VECTOR_H__

#include <stdlib.h>
#include <algorithm>


template <typename T>
class my_vector {
    size_t my_size;
    size_t my_capacity;
    T* my_data;
    
public:
    explicit my_vector();
    explicit my_vector(size_t inp_size, const T& inp_value = T());
    my_vector(const my_vector<T>& inp_vector);
    
    ~my_vector();
    
    my_vector<T>& operator=(const my_vector<T>& inp_my_vector);
    
    size_t size() const;
    void resize(size_t inp_new_size, const T& inp_value = T());
    
    size_t capacity() const;
    void reserve(size_t inp_new_capacity);
    
    bool empty() const;
    
    T& operator[](size_t inp_position);
    const T& operator[](size_t inp_position) const;
    
    T& front();
    const T& front() const;
    
    T& back();
    const T& back() const;
    
    void push_back(const T& inp_value);
    void pop_back();
    
    void swap(my_vector<T>& inp_my_vector);
    
    void clear();
    
    
};

template <typename T>
my_vector<T>::my_vector():my_size(0), my_capacity(0){}


template <typename T>
my_vector<T>::my_vector(size_t inp_size, const T& inp_value):my_size(inp_size), my_capacity(inp_size){
    this->my_data = new T[this->my_capacity];
    for(size_t i = 0; i < this->size(); i++)
        (*this)[i] = inp_value;
    return;
}


template <typename T>
my_vector<T>::my_vector(const my_vector<T>& inp_vector):my_size(inp_vector.size()), my_capacity(inp_vector.capacity()){
    this->my_data = new T[this->my_capacity];
    for(size_t i = 0; i < this->size(); i++)
        (*this)[i] = inp_vector[i];
    return;
}

template <typename T>
my_vector<T>::~my_vector(){
    delete[] my_data;
}


template <typename T>
size_t my_vector<T>::size() const{
    return this->my_size;
}

template <typename T>
size_t my_vector<T>::capacity() const{
    return this->my_capacity;
}


template <typename T>
void my_vector<T>::resize(size_t inp_new_size, const T& inp_value){
    if (inp_new_size > this->my_capacity){
        size_t new_capacity = this->my_capacity * 2;
        if (inp_new_size > new_capacity)
            new_capacity = inp_new_size;
        T* new_data = new T[new_capacity];
        for (size_t i = 0; i < this->size(); i++)
            new_data[i] = (*this)[i];
        delete[] this->my_data;
        this->my_data = new_data;
        this->my_capacity = new_capacity;
        this->my_size = inp_new_size;
    }
    else{
        this->my_size = inp_new_size;
    }
    return;
}

template <typename T>
void my_vector<T>::reserve(size_t inp_new_capacity){
    if (inp_new_capacity > this->my_capacity){
        T* new_data = new T[inp_new_capacity];
        for (size_t i = 0; i < this->size(); i++)
            new_data[i] = (*this)[i];
        delete[] this->my_data;
        this->my_data = new_data;
        this->my_capacity = inp_new_capacity;
    }
    return;
}


template <typename T>
T& my_vector<T>::operator[](size_t inp_position){
    return my_data[inp_position];
}

template <typename T>
const T& my_vector<T>::operator[](size_t inp_position) const{
    return my_data[inp_position];
}

template <typename T>
bool my_vector<T>::empty() const{
    return this->size() == 0;
}

template <typename T>
void my_vector<T>::swap(my_vector<T>& inp_my_vector){
    std::swap(this->my_data,inp_my_vector.my_data);
    std::swap(this->my_size,inp_my_vector.my_size);
    std::swap(this->my_capacity,inp_my_vector.my_capacity);
}

template <typename T>
my_vector<T>& my_vector<T>::operator=(const my_vector<T>& inp_my_vector){
    my_vector tmp(inp_my_vector);
    this->swap(tmp);
    return *this;
}

template <typename T>
void my_vector<T>::clear(){
    this->resize(0);
    return;
}

template <typename T>
void my_vector<T>::push_back(const T& inp_value){
    this->resize(this->size()+1);
    (*this)[this->size()-1] = inp_value;
    return;
}

template <typename T>
void my_vector<T>::pop_back(){
    this->resize(this->size()-1);
    return;
}

template <typename T>
T& my_vector<T>::front(){
    return (*this)[0];
}

template <typename T>
const T& my_vector<T>::front() const{
    return (*this)[0];
}

template <typename T>
T& my_vector<T>::back(){
    return (*this)[this->size()-1];
}

template <typename T>
const T& my_vector<T>::back() const{
    return (*this)[this->size()-1];
}



#endif /* __MY_VECTOR_H__ */